﻿using System.Collections.Generic;
using System.ComponentModel;
using BaseTypesLib;
using Microsoft.Practices.Prism.Events;
using BBInterfaceNET.Events;
using BBInterfaceNET.CommonServices;
using System.Diagnostics;
using BaseTypesLib.Model;
using Microsoft.Practices.Prism.Commands;
using System.Windows.Input;
using System.Linq;
using System.Reflection;
using System;
using Microsoft.Practices.Unity;
using BBInterfaceNET.Designer.Services;
using BBInterfaceNET.Events.Model;

namespace BBInterfaceNET.Designer.ViewModels
{
    public class FileDesignerViewModel:DocumentViewModel
    {
        bool isDirty;
        
        //the currently selected field
        private Field selectedField;
        //the screen hierarchy
        private Field theScreen;
        //the designer size
        private int displayWidth, displayHeight;
        private IEventAggregator eventAggregator;
        private IDesignerPersistenceService persistenceService;
        private IUnityContainer container;
        private IAddFieldService addFieldService;

        private DelegateCommand upCmd, downCmd;
        //the selected field from the treeview
        private DelegateCommand<object> selCmd;
        private DelegateCommand addCmd, deleteCmd;
        private bool isExpanded;
        private DelegateCommand expandCmd;
        private Assembly baseTypesAsm;
        private int fontSize;

        private DelegateCommand addBanner, addTitle, addStatus;

        public FileDesignerViewModel(IEventAggregator eventAggregator, 
            IDesignerPersistenceService persistenceService, IUnityContainer container,
            IAddFieldService addFieldService)
        {
            this.addFieldService = addFieldService;
            this.container = container;
            this.eventAggregator = eventAggregator;
            this.persistenceService = persistenceService;

            eventAggregator.GetEvent<FieldChangedEvent>().Subscribe(OnFieldPropertiesChanged);
        }
        public void OnFieldPropertiesChanged(FieldChangedData data)
        {
            if (IsActive)
                IsDirty = true;
        }
        protected override void ShutdownOverride(CancelEventArgs args)
        {
            if (IsDirty) args.Cancel = true;
        }

        #region properties
        public int DisplayWidth
        {
            get { return displayWidth; }
            set
            {
                if (displayWidth != value)
                {
                    displayWidth = value;
                    RaisePropertyChanged("DisplayWidth");
                }
            }
        }
        public int DisplayHeight
        {
            get { return displayHeight; }
            set
            {
                if (displayHeight != value)
                {
                    displayHeight = value;
                    RaisePropertyChanged("DisplayHeight");
                }
            }
        }
        public int FontSize
        {
            get { return fontSize; }
            set
            {
                if (fontSize != value)
                {
                    fontSize = value;
                    RaisePropertyChanged("FontSize");
                }
            }
        }
        
        public Field Screen
        {
            get
            {
                if (theScreen == null)
                {
                    theScreen = persistenceService.LoadFile(FilePath);
                    //register all screen fields for change notification
                }
                return theScreen;
            }
        }
        
        public bool IsDirty
        {
            get { return isDirty; }
            set
            {
                if (isDirty != value)
                {
                    isDirty = value;
                    RaisePropertyChanged(() => IsDirty);
                    SaveCommand.RaiseCanExecuteChanged();
                }
            }
        }
        #endregion

        #region save overrides
        protected override void SaveOverride()
        {
            //save the file
            bool res = persistenceService.Save(FilePath, Screen as MainScreen);
            if (res)
                IsDirty = false;
            else
                Debug.WriteLine("Error saving the file");
        }
        protected override bool CanSaveOverride
        {
            get { return IsDirty; }
        }
        #endregion

        #region close overrides
        protected override void CloseOverride(CancelEventArgs args)
        {
            if (IsDirty)
            {
                IConfirmationService service = container.Resolve<IConfirmationService>();
                service.ShowConfirmationDialog("Close File", "The file has been modified. Do you want to save before closing?",
                    res => {
                        if (res == null)
                            args.Cancel = true;
                        else if (res != null && res.Value)
                        {//save
                            SaveOverride();
                        }
                        else{/*don't save*/}
                    });
            }
        }
        #endregion

        #region IActiveAware overrides
        protected override void OnIsActiveChanged()
        {
            base.OnIsActiveChanged();

            SaveCommand.IsActive = IsActive;
            //publish the selected field
            if (IsActive)
            {
                if (Screen != null)
                    eventAggregator.GetEvent<DocumentChangedEvent>().Publish(Screen as MainScreen);

                eventAggregator.GetEvent<FieldSelectedEvent>().Publish(selectedField);                
            }
        }
        #endregion

        public void InvalidateCommands()
        {
            addCmd.RaiseCanExecuteChanged();
            deleteCmd.RaiseCanExecuteChanged();
            upCmd.RaiseCanExecuteChanged();
            downCmd.RaiseCanExecuteChanged();
        }
        public void FieldDropped(Field field)
        {
            if (SelectedField != null)
            {
                SelectedField.IsSelected = false;
            }
            SelectedField = field;
            SelectedField.IsSelected = true;
            IsDirty = true;
            InvalidateCommands();
            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(SelectedField);
        }
        public void SelectionChanged(Field field)
        {
            if (SelectedField == field) return;
            if (SelectedField != null)
                SelectedField.IsSelected = false;
            SelectedField = field;
            SelectedField.IsSelected = true;
            InvalidateCommands();
            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(SelectedField);
        }

        public void ClearSelection()
        {
            if (SelectedField != null)
            {
                SelectedField.IsSelected = false;
                SelectedField = null;
            }
            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(null);
        }

        #region banner, title, status
        public List<Field> Banner
        {
            get
            {
                MainScreen main=(MainScreen)Screen;
                if (main.Banner == null)
                    return null;
                else
                    return new List<Field>() { main.Banner };
            }
        }
        public List<Field> Title
        {
            get
            {
                MainScreen main = (MainScreen)Screen;
                if (main.Title == null)
                    return null;
                else
                    return new List<Field>() { main.Title };
            }
        }
        public List<Field> Status
        {
            get
            {
                MainScreen main = (MainScreen)Screen;
                if (main.Status == null)
                    return null;
                else
                    return new List<Field>() { main.Status };
            }
        }

        public ICommand AddBannerCommand
        {
            get
            {
                if (addBanner == null)
                    addBanner = new DelegateCommand(OnAddBanner);
                return addBanner;
            }
        }
        public ICommand AddTitleCommand
        {
            get
            {
                if (addTitle == null)
                    addTitle = new DelegateCommand(OnAddTitle);
                return addTitle;
            }
        }
        public ICommand AddStatusCommand
        {
            get
            {
                if (addStatus == null)
                    addStatus = new DelegateCommand(OnAddStatus);
                return addStatus;
            }
        }

        private void OnAddBanner()
        {
            Field f = GetTypeFromDialog();
            if (f != null)
            {
                ((MainScreen)Screen).Banner = f;
                RaisePropertyChanged("Banner");
            }
        }
        private void OnAddTitle()
        {
            Field f = GetTypeFromDialog();
            if (f != null)
            {
                ((MainScreen)Screen).Title = f;
                RaisePropertyChanged("Title");
            }
        }
        private void OnAddStatus()
        {
            Field f = GetTypeFromDialog();
            if (f != null)
            {
                ((MainScreen)Screen).Status = f;
                RaisePropertyChanged("Title");
            }
        }

        #endregion

        #region treeview up & down
        public ICommand UpCommand
        {
            get
            {
                if (upCmd == null)
                    upCmd = new DelegateCommand(OnUpCommand, CanUp);
                return upCmd;
            }

        }
        private void OnUpCommand()
        {
            Field sItem = selectedField;
            Manager mgr = sItem.Manager;

            if (mgr == null) return;

            int idx = mgr.Fields.ToList().IndexOf(sItem);
            mgr.RemoveField(sItem);

            mgr.InsertField(idx - 1, sItem);
            IsDirty = true;
            InvalidateCommands();
        }
        private bool CanUp()
        {
            if (selectedField != null && selectedField.Manager != null && selectedField.Manager.Fields.Count > 1 &&
                selectedField.Manager.Fields.ToList().IndexOf(selectedField) > 0)
                return true;

            return false;
        }

        public ICommand DownCommand
        {
            get
            {
                if (downCmd == null)
                    downCmd = new DelegateCommand(OnDownCommand, CanDown);
                return downCmd;
            }

        }
        private void OnDownCommand()
        {
            Field sItem = selectedField;
            Manager mgr = sItem.Manager;

            if (mgr == null) return;

            int idx = mgr.Fields.ToList().IndexOf(sItem);
            mgr.RemoveField(sItem);

            mgr.InsertField(idx + 1, sItem);
            IsDirty = true;
            InvalidateCommands();
        }
        private bool CanDown()
        {
            if (selectedField != null && selectedField.Manager != null && 
                selectedField.Manager.Fields.Count > 1 &&
                selectedField.Manager.Fields.ToList().IndexOf(selectedField) < (selectedField.Manager.Fields.Count - 1))
                return true;

            return false;
        }
        #endregion

        #region treeview add & delete
        public ICommand AddCommand
        {
            get
            {
                if (addCmd == null)
                    addCmd = new DelegateCommand(OnAdd, CanAdd);
                return addCmd;
            }
        }
        private Field GetTypeFromDialog()
        {
            AddFieldViewModel vm = container.Resolve<AddFieldViewModel>();
            bool res = addFieldService.ShowAddFieldView(vm);
            if (!res) return null;

            //string typeName = vm.SelectedItem.Type.AssemblyQualifiedName;
            //if (baseTypesAsm == null)
            //    baseTypesAsm = Assembly.Load("BaseTypesLib");
            //Type type = baseTypesAsm.GetType(typeName);
            Type type = vm.SelectedItem.Type;
            if (type == null) return null;
            ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes);
            if (ci != null)
            {
                object obj = ci.Invoke(null);
                return obj as Field;
            }
            return null;
        }
        private void OnAdd()
        {
            Field f = GetTypeFromDialog();
            if (f != null)
                AddFieldToScreen(f);
        }
        private void AddFieldToScreen(Field field)
        {
            field.IsSelected = true;
            
            Manager mgr = SelectedField as Manager;
            if (mgr != null)
            {
                mgr.AddField(field);
            }
            else
            {
                SelectedField.Manager.AddField(field);
            }

            if (SelectedField != null)
                SelectedField.IsSelected = false;
            SelectedField = field;
            IsDirty = true;
            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(SelectedField);
            InvalidateCommands();
        }
        private bool CanAdd()
        {
            if (SelectedField == null) return false;
            return true;
        }

        public ICommand DeleteCommand
        {
            get
            {
                if (deleteCmd == null)
                    deleteCmd = new DelegateCommand(OnDelete, CanDelete);
                return deleteCmd;
            }
        }
        public void OnDelete()
        {
            if (SelectedField == null) return;
            MainScreen screen=Screen as MainScreen;

            if (SelectedField.Manager != null)
            {
                SelectedField.Manager.RemoveField(SelectedField);   
            }
            else if (SelectedField == screen.Banner)
            {
                screen.Banner = null;
                RaisePropertyChanged("Banner");
            }
            else if (SelectedField == screen.Title)
            {
                screen.Title = null;
                RaisePropertyChanged("Title");
            }
            else if (SelectedField == screen.Status)
            {
                screen.Status = null;
                RaisePropertyChanged("Status");
            }

            SelectedField = null;
            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(null);
            IsDirty = true;
            InvalidateCommands();
        }
        public bool CanDelete()
        {
            if (SelectedField == null || SelectedField is MainScreen) return false;
            return true;
        }
        #endregion


        public ICommand SelectedCommand
        {
            get
            {
                if (selCmd == null)
                    selCmd = new DelegateCommand<object>(OnSelectionChanged, p => { return true; });
                return selCmd;
            }
        }
        protected Field SelectedField
        {
            get { return selectedField; }
            set
            {
                if (selectedField == value)
                    return;
                selectedField = value;
            }
        }
        private void OnSelectionChanged(object data)
        {
            if (!(data is Field) || data==SelectedField) 
                return;
            if (SelectedField != null)
                SelectedField.IsSelected = false;
            SelectedField = data as Field;
            SelectedField.IsSelected = true;

            InvalidateCommands();

            eventAggregator.GetEvent<FieldSelectedEvent>().Publish(SelectedField);
        }
        
        public ICommand ExpandCommand
        {
            get
            {
                if (expandCmd == null)
                    expandCmd = new DelegateCommand(() =>
                    {
                        IsExpanded = !IsExpanded;
                    }, () => { return theScreen != null; });
                return expandCmd;
            }
        }
        public bool IsExpanded
        {
            get { return isExpanded; }
            set
            {
                if (isExpanded != value)
                {
                    isExpanded = value;
                    RaisePropertyChanged(() => IsExpanded);
                    RaisePropertyChanged(() => ExpandedString);
                }
            }
        }
        public string ExpandedString
        {
            get { return isExpanded ? "-" : "+"; }
        }
    }
}
